<?php

namespace App\Http\Requests;

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class Base extends FormRequest
{

    public array $scenes = [];
    public string $currentScene;             //当前场景
    public bool $autoValidate = false;       //是否注入之后自动验证
    public array $extendRules = [];

    public function authorize(): bool
    {
        return true;
    }


    /**
     * 设置场景
     *
     * @param $scene
     * @return $this
     */
    public function scene($scene): static
    {
        $this->currentScene = $scene;
        return $this;
    }


    /**
     * 使用扩展rule
     *
     * @param string $name
     * @return $this
     */
    public function with(string $name = ''): static
    {
        if (is_array($name)) {
            $this->extendRules = array_merge($this->extendRules[], array_map(function ($v) {
                return Str::camel($v);
            }, $name));
        } else if (is_string($name)) {
            $this->extendRules[] = Str::camel($name);
        }
        return $this;
    }


    /**
     * 覆盖自动验证方法
     *
     */
    public function validateResolved(): void
    {
        if ($this->autoValidate) {
            try {
                $this->handleValidate();
            } catch (AuthorizationException|ValidationException $e) {
            }
        }
    }

    /**
     * 最终验证方法
     *
     * @throws AuthorizationException
     * @throws ValidationException
     */
    protected function handleValidate(): void
    {
        if (!$this->passesAuthorization()) {
            $this->failedAuthorization();
        }
        $instance = $this->getValidatorInstance();
        if ($instance->fails()) {
            $this->failedValidation($instance);
        }
    }

    /**
     * 验证失败
     *
     * @param Validator $validator
     */
    protected function failedValidation(Validator $validator)
    {
        $error = $validator->errors()->all();
        throw new HttpResponseException(response()->json([
            'code' => 400,
            'status' => false,
            'message' => $error[0],
            'data' => [],
            "timestamp" => time(),
        ]));
    }

    /**
     * 验证方法
     *
     * @param string $scene
     * @return void
     * @throws AuthorizationException
     * @throws ValidationException
     */
    public function validate(string $scene = ''): void
    {
        if ($scene) {
            $this->currentScene = $scene;
        }
        $this->handleValidate();
    }

    /**
     * 覆盖设置 自定义验证器
     *
     * @param $factory
     * @return mixed
     */
    public function validator($factory): mixed
    {
        return $factory->make(
            $this->validationData(), $this->getRules(),
            $this->messages(), $this->attributes()
        );
    }

    /**
     * 根据场景获取规则
     *
     * @return mixed
     */
    public function getRules(): mixed
    {
        $rules = $this->container->call([$this, 'rules']);
        $newRules = [];
        if ($this->extendRules) {
            $extendRules = array_reverse($this->extendRules);
            foreach ($extendRules as $extendRule) {
                if (method_exists($this, "{$extendRule}Rules")) {   //合并场景规则
                    $rules = array_merge($rules, $this->container->call(
                        [$this, "{$extendRule}Rules"]
                    ));
                }
            }
        }
        if ($this->currentScene && isset($this->scenes[$this->currentScene])) {
            $sceneFields = is_array($this->scenes[$this->currentScene])
                ? $this->scenes[$this->currentScene] : explode(',', $this->scenes[$this->currentScene]);
            foreach ($sceneFields as $field) {
                if (array_key_exists($field, $rules)) {
                    $newRules[$field] = $rules[$field];
                }
            }
            return $newRules;
        }
        return $rules;
    }

}
